home *** CD-ROM | disk | FTP | other *** search
Text File | 1994-09-15 | 11.4 KB | 355 lines | [TEXT/MMCC] |
- // ===========================================================================
- // CTwistDownListBox.h ©1994 Jan Bruyndonckx All rights reserved.
- // v1.0 18/6/94
- // Inspired by M.Minow's article in Develop 18
- //
- // A hierarchical listbox subclass.
- // The default listbox contains only string data.
- // ===========================================================================
-
- #include <types.h>
- #include <strings.h>
- #include <string.h>
- #include <Palettes.h>
- #include "CTwistDownListBox.h"
-
- //----------------------------------------------------------------------------
- // The trianges
-
- PolyHandle CTwistDownListBox::sClosedPoly = NULL,
- CTwistDownListBox::sOpenedPoly = NULL,
- CTwistDownListBox::sIntermediatePoly = NULL ;
-
- static void DrawTriangle (PolyHandle aPoly, const Boolean drawFilled,
- const short posV, const short posH) ;
-
- // These macros simplify access to the flag word in the list element.
- #define SetTDFlag(flags, mask) ((flags) |= (mask))
- #define ClearTDFlag(flags, mask) ((flags) &= ~(mask))
- #define InvertTDFlag(flags, mask) ((flags) ^= (mask))
- #define TestTDFlag(flags, mask) (((flags) & (mask)) != 0)
-
- typedef enum { // Misc. constants
- kTriangleOutsideGap = 1, // From margin to button
- kTriangleInsideGap = 3, // From button to text
- kScrollBarWidth = 16, // width of right scroll bar
- kAnimationDelay = 3, // the time to show the intermediate button
- kIndentOffset = 16 // to offset each indentation level by
- } MiscConstants ;
-
- //----------------------------------------------------------------------------
- // Creation methods
-
- CTwistDownListBox* CTwistDownListBox::CreateFromStream(LStream *inStream)
- {
- return (new CTwistDownListBox(inStream));
- }
-
- CTwistDownListBox::CTwistDownListBox() : CCustomListBox()
- {
- init () ;
- }
-
- CTwistDownListBox::CTwistDownListBox(const SPaneInfo &inPaneInfo,
- Boolean inHasHorizScroll, Boolean inHasVertScroll,
- Boolean inHasGrow, Boolean inHasFocusBox,
- MessageT inDoubleClickMessage, Int16 inTextTraitsID,
- LCommander *inSuper) :
- CCustomListBox (inPaneInfo, inHasHorizScroll, inHasVertScroll,
- inHasGrow, inHasFocusBox, inDoubleClickMessage, inTextTraitsID,
- inSuper)
- {
- init () ;
- }
-
- CTwistDownListBox::CTwistDownListBox(LStream *inStream) : CCustomListBox (inStream)
- {
- init () ;
- }
-
- //----------------------------------------------------------------------------
- // Create the triangle buttons, if they didn't exist already
-
- void CTwistDownListBox::init (void)
- {
- // Create the twist down buttons
- // Note that the port and text drawing characteristics must have been set.
-
- short buttonSize;
- short halfSize;
- short intermediateSize;
- FontInfo info;
-
- ::GetFontInfo(&info);
- buttonSize = info.ascent; // The "show sublist" button
- buttonSize &= ~1; // Round down to an even number
- halfSize = buttonSize / 2;
- intermediateSize = (buttonSize * 3) / 4;
-
- // note, we allocate the polygons once (they are static variables) for *all* lists, and
- // we never dispose them
-
- if (sClosedPoly == NULL)
- { sClosedPoly = ::OpenPoly();
- ::MoveTo(halfSize, 0);
- ::LineTo(buttonSize, halfSize);
- ::LineTo(halfSize, buttonSize);
- ::LineTo(halfSize, 0);
- ::ClosePoly();
- }
- if (sOpenedPoly == NULL)
- { sOpenedPoly = ::OpenPoly();
- ::MoveTo(0, halfSize);
- ::LineTo(buttonSize, halfSize);
- ::LineTo(halfSize, buttonSize);
- ::LineTo(0, halfSize);
- ::ClosePoly();
- ::OffsetPoly (sOpenedPoly, 1, -2) ;
- }
- if (sIntermediatePoly == NULL)
- { sIntermediatePoly = ::OpenPoly();
- ::MoveTo(intermediateSize, 0);
- ::LineTo(intermediateSize, intermediateSize);
- ::LineTo(0, intermediateSize);
- ::LineTo(intermediateSize, 0);
- ::ClosePoly();
- }
-
- // Remember the width of the "button" area.
- triangleWidth = (**sOpenedPoly).polyBBox.right
- + kTriangleOutsideGap
- + kTriangleInsideGap;
- }
-
- //----------------------------------------------------------------------------
- // If the data-field contains text (default case), then get/set it
-
- StringPtr CTwistDownListBox::GetDescriptor(Str255 outDescriptor) const
- { Byte buffer[260] ;
- short l = sizeof (buffer) ;
- TwistDownRecPtr twistElement = (TwistDownRecPtr) buffer ;
-
- twistElement->data[0] = '\0' ;
- GetSelection (twistElement, &l) ;
- l -= TwistDownRecSize ;
- ::memcpy (outDescriptor+1, twistElement->data, l) ;
- outDescriptor[0] = l ;
-
- return outDescriptor ;
- }
-
- void CTwistDownListBox::SetDescriptor(ConstStr255Param inDescriptor)
- { Byte buffer[260] ;
- short l ;
- TwistDownRecPtr twistElement = (TwistDownRecPtr) buffer ;
-
- l = *inDescriptor+1 ;
- ::memcpy (twistElement->data, inDescriptor+1, l) ;
- twistElement->indent = 0 ;
- twistElement->flags = 0 ;
- l += TwistDownRecSize ;
- SetSelection (twistElement, l) ;
- }
-
- //----------------------------------------------------------------------------
- // The user clicked in the list
-
- void CTwistDownListBox::ClickSelf(const SMouseDownEvent &inMouseDown)
- { long l ;
-
- // set target to the right pane
- SwitchTarget(this);
- FocusDraw();
-
- // is the mousedown in the twistDown button area?
- Point mousePt = inMouseDown.macEvent.where ;
- ::GlobalToLocal (&mousePt) ;
- Rect hitRect = (*mMacListH)->rView ;
- hitRect.right += kScrollBarWidth ;
- if (::PtInRect (mousePt, &hitRect) == false) // click is not in our pane
- return ;
- hitRect.right = (*mMacListH)->rView.left + triangleWidth ;
- if (::PtInRect (mousePt, &hitRect)) // yes, is it in a button?
- { short visibleTop = (*mMacListH)->visible.top ;
- short cellHeight = (*mMacListH)->cellSize.v ;
- Cell theCell ; // the selected cell
- theCell.v = (mousePt.v - hitRect.top) / cellHeight + visibleTop ;
- theCell.h = 0 ;
- TwistDownRecPtr twist = (TwistDownRecPtr) GetCellPtr (theCell) ;
-
- if ((twist == NULL) || (TestTDFlag(twist->flags, kHasSubList) == 0))
- goto end ;
-
- SetTDFlag (twist->flags, kDrawFilled) ; // Draw the button
- ::LDraw (theCell, mMacListH) ; // Draw the cell anew
- hitRect.top += ((theCell.v - visibleTop) * cellHeight) ; // set to the dimensions of the button
- hitRect.bottom = hitRect.top + cellHeight ;
- Boolean inHitRect = false ;
- if (::StillDown())
- { inHitRect = true ;
- while (::WaitMouseUp())
- { ::GetMouse (&mousePt);
- Boolean newInHitRect = ::PtInRect(mousePt, &hitRect) ;
- if (newInHitRect != inHitRect)
- { twist = (TwistDownRecPtr) GetCellPtr (theCell) ;
- InvertTDFlag (twist->flags, kDrawFilled) ;
- ::LDraw (theCell, mMacListH) ;
- inHitRect = newInHitRect ;
- }
- }
- }
-
- twist = (TwistDownRecPtr) GetCellPtr (theCell) ; // the user released the mouse
- if (inHitRect == false)
- { // make very sure the button is cleared
- if (TestTDFlag (twist->flags, kDrawFilled))
- { ClearTDFlag (twist->flags, kDrawFilled) ;
- ::LDraw (theCell, mMacListH) ;
- }
- return ; // and get out…
- }
-
- SetTDFlag (twist->flags, kDrawIntermediate) ; // Draw the animation triangle
- ::LDraw (theCell, mMacListH) ;
- ::Delay (kAnimationDelay, &l) ;
- twist = (TwistDownRecPtr) GetCellPtr (theCell) ;
- ClearTDFlag (twist->flags, kDrawIntermediate) ;
-
- ::LSetDrawingMode (false, mMacListH) ; // here send message…
- if (TestTDFlag (twist->flags, kIsOpened)) // …to expand/collapse sublist
- { ClearTDFlag (twist->flags, kIsOpened) ;
- CollapseElement (theCell) ;
- }
- else
- { SetTDFlag (twist->flags, kIsOpened) ;
- ExpandElement (theCell) ;
- }
- ::LSetDrawingMode (true, mMacListH) ; // redraw list again…
- twist = (TwistDownRecPtr) GetCellPtr (theCell) ;
- ClearTDFlag (twist->flags, kDrawFilled) ;
- hitRect = (*mMacListH)->rView ;
- ResizeFrameBy (0, 0, true) ; // this works more correctly when collapsing & rows are 'above' the top
- return ;
- }
-
- end: // mousedown was not in a button…
- inherited::ClickSelf (inMouseDown) ;
- }
-
- //----------------------------------------------------------------------------
-
- void CTwistDownListBox::DrawElementSelf (const Rect *lRect,
- const void *lElement,
- const short lDataLen)
-
- // Draw a single list cell on the screen.
- // Checks flags and draws triangular button if needed;
- // calls DrawTwistedElement to draw cell contents.
-
- {
- TwistDownRecPtr twistElement = (TwistDownRecPtr) lElement ;
-
- if (TestTDFlag (twistElement->flags, kHasSubList))
- { PolyHandle aPoly = NULL ;
-
- aPoly = TestTDFlag(twistElement->flags, kIsOpened) ? sOpenedPoly : sClosedPoly ;
- if (TestTDFlag(twistElement->flags, kDrawIntermediate))
- aPoly = sIntermediatePoly ;
- if (aPoly)
- DrawTriangle (aPoly,
- TestTDFlag(twistElement->flags, kDrawFilled),
- lRect->top+1,
- lRect->left+kTriangleOutsideGap) ;
- }
-
- ::MoveTo (lRect->left + triangleWidth + 2 + twistElement->indent*kIndentOffset,
- lRect->top + 10) ;
- DrawTwistedElement (lRect, twistElement, lDataLen) ;
- }
-
- //----------------------------------------------------------------------------
-
- void CTwistDownListBox::DrawTwistedElement (const Rect *lRect,
- const TwistDownRecPtr twistElement,
- const short lDataLen)
-
- // Draw contents of a single list element.
- // Default version just draws text; override for other types of data.
-
- {
- ::DrawText (twistElement->data, 0, lDataLen-TwistDownRecSize) ;
- }
-
- //----------------------------------------------------------------------------
- // User closed a sublist. Remove all sublists hanging from this element.
- // Override in case an element contains a pointer to some allocated memory.
-
- void CTwistDownListBox::CollapseElement (const Cell theCell)
- { short count = 0 ;
- TwistDownRecPtr twist = (TwistDownRecPtr) GetCellPtr (theCell) ;
- short headIndent = twist->indent ;
- Cell cell = theCell ;
-
- for (cell.v++ ; ; cell.v++)
- { twist = (TwistDownRecPtr) GetCellPtr (cell) ;
- if (twist == NULL)
- break ;
- if (twist->indent <= headIndent)
- break ;
- count++ ;
- }
-
- if (count)
- ::LDelRow (count, theCell.v+1, mMacListH) ;
- }
-
- //----------------------------------------------------------------------------
- // You'd better override me!
-
- void CTwistDownListBox::ExpandElement (const Cell theCell)
- {
- }
-
- //----------------------------------------------------------------------------
-
- static void DrawTriangle (PolyHandle aPoly, const Boolean drawFilled,
- const short posV, const short posH)
- { const short kGrayness = 4 ; // 1 for intermediate gray, 8 for very light gray
-
- ::OffsetPoly (aPoly, posH, posV); // move poly to the right spot
-
- if (drawFilled)
- ::FillPoly(aPoly, &qd.black);
- else
- { RGBColor foreColor;
- RGBColor saveForeColor;
- RGBColor backColor;
-
- ::GetForeColor(&foreColor);
- ::GetBackColor(&backColor);
- saveForeColor = foreColor;
-
- // This loop sets foreColor to a very light gray.
- for (short i = 0; i < kGrayness; i++)
- if (::GetGray(GetGDevice(), &backColor, &foreColor) == false)
- break;
-
- if ((foreColor.red == 0) &&
- (foreColor.green == 0) &&
- (foreColor.blue == 0))
- ::ErasePoly (aPoly) ;
- else
- { ::RGBForeColor(&foreColor);
- ::FillPoly(aPoly, &qd.black);
- ::RGBForeColor(&saveForeColor);
- }
-
- ::FramePoly (aPoly) ;
- }
-
- ::OffsetPoly (aPoly, -posH, -posV);
- }
-
- //----------------------------------------------------------------------------
-
-